home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Magazine / Online / httpproxy / src / cache.c next >
C/C++ Source or Header  |  1996-08-20  |  28KB  |  994 lines

  1. /*(( "Header" */
  2. /*
  3.  * $Id: cache.c,v 1.6 1996/08/20 17:36:43 mshopf Exp mshopf $
  4.  *
  5.  * (c) 1995-96 Matthias Hopf
  6.  *
  7.  * Cache file maintainance for HttpProxy.
  8.  *
  9.  */
  10.  
  11. /*
  12.  * $Log: cache.c,v $
  13.  * Revision 1.6  1996/08/20  17:36:43  mshopf
  14.  * *very* small bug fix...
  15.  *
  16.  * Revision 1.5  1996/08/12  03:33:36  mshopf
  17.  * on delete checking for related file (return 7).
  18.  *
  19.  * Revision 1.4  1996/08/11  22:25:15  mshopf
  20.  * reworked debug messages.
  21.  *
  22.  * Revision 1.3  1996/07/30  13:57:03  mshopf
  23.  * bug fixes.
  24.  * better escaping scheme.
  25.  * deleteing all files in @temp and @trash on startup now.
  26.  * removing empty cache files automatically.
  27.  *
  28.  * Revision 1.2  1996/07/19  19:59:55  mshopf
  29.  * hash function bugfix.
  30.  *
  31.  * Revision 1.1  1996/07/17  16:42:42  mshopf
  32.  * Initial revision
  33.  *
  34.  */
  35.  
  36.  
  37. /*)) */
  38. /*(("Includes/Constants" */
  39.  
  40. #include <ctype.h>
  41. #include <string.h>
  42.  
  43. #include <exec/exec.h>
  44. #include <dos.h>
  45. #include <dos/exall.h>
  46. #include <dos/datetime.h>
  47.  
  48. #include <proto/exec.h>
  49. #include <proto/dos.h>
  50.  
  51. #include "httpproxy.h"
  52. #include "logging.h"
  53. #include "cache.h"
  54.  
  55. /*)) */
  56. /*(( "Globals" */
  57.  
  58. /* Escape character tables */
  59. /* Entries specify the escaping character / the escaped character,
  60.  * '\0' for valid characters / not escaped chars */
  61.  
  62. static char InvalidChars [256];            /* initialized with '\0' */
  63. static char EscapedChars [256];            /* '' */
  64.  
  65. /*)) */
  66.  
  67. /*(( "EscapePartial ()" */
  68.  
  69. /* private: Escape special chars in file/dir part.
  70.  * returns: 0: not possible, 1: final filname part reached 3: intermediate dir part */
  71. /* Url and Dir pointers are updated accordingly (only on return value != 0). */
  72. /* The first two parts may have special treatment (protocol and host) */
  73.  
  74. static int EscapePartial (const char **UrlPtr, char **DirPtr, int PartNumber)
  75. {
  76.     int len = MAX_FILE_LEN - 3;
  77.     const char *Url;
  78.     char       *Dir;
  79.     char c;
  80.  
  81.     Url = *UrlPtr;
  82.     Dir = *DirPtr;
  83.  
  84.     // TODO: special treatment for host part for ':'
  85.     for (;;)
  86.     {
  87.     if (len <= 0)
  88.         dreturn (D_CACHE, 0);      /* perhaps TODO: break long url parts into multiple parts . */
  89.     c = *Url++;
  90.     if (! InvalidChars [c])
  91.     {
  92.         if (PartNumber == 1)       /* host name is case insensitive */
  93.         *Dir++ = tolower (c);
  94.         else
  95.         *Dir++ = c;
  96.         len--;
  97.     }
  98.     else if (c == '/')             /* reached part end */
  99.     {
  100.         if (Dir == *DirPtr)        /* empty directory name? */
  101.         {
  102.         *Dir++ = '@';
  103.         *Dir++ = '!';
  104.         }
  105.         *Dir = '\0';
  106.         *DirPtr = Dir;
  107.         *UrlPtr = Url;
  108.         dreturn (D_CACHE, 3);
  109.     }
  110.     else if (c == ':' && PartNumber == 0)   /* special treatment: protocoll part */
  111.     {
  112.         if (Url [0] == '/' && Url [1] == '/')
  113.         {
  114.         *Dir = '\0';
  115.         *DirPtr = Dir;
  116.         *UrlPtr = Url + 2;     /* skip :// part */
  117.         dreturn (D_CACHE, 3);
  118.         }
  119.         dreturn (D_CACHE, 0);      /* no standard protocoll part - skip it */
  120.     }
  121.     else if (c == '\0')            /* reached file end */
  122.     {
  123.         *Dir = '\0';
  124.         *DirPtr = Dir;
  125.         *UrlPtr = Url;
  126.         dreturn (D_CACHE, 1);
  127.     }
  128.     else                           /* escape special character */
  129.     {
  130.         *Dir++ = '!';
  131.         *Dir++ = InvalidChars [c];
  132.         assert (! InvalidChars [InvalidChars [c]]);
  133.         len -=2;
  134.     }
  135.     }
  136. }
  137.  
  138. /*)) */
  139. /*(( "HashPartial ()" */
  140.  
  141. /* private: Hash partial file name.
  142.  * returns: 0: not possible, 2: final filename part reached 4: intermediate dir part */
  143. /* Url and Dir pointers are updated accordingly (only on return value != 0). */
  144. /* The first two parts may have special treatment (protocol and host) */
  145.  
  146. static int HashPartial (const char **UrlPtr, char **DirPtr, const char *FullUrl, int PartNumber)
  147. {
  148.     const char *Url, *Last;
  149.     int  Hash = 0;
  150.     char __aligned Bytes [4] = { 0, 0, 0, 0 };
  151. #if (sizeof(int) != 4)
  152. #   error Only 4 byte ints supported!
  153. #endif
  154.  
  155.     Url = *UrlPtr;
  156.  
  157.     if (! (Last = strchr (Url, '/')) )       /* check end of partial url */
  158.     Last = Url + strlen (Url);
  159.  
  160.     /* Hash values are always computed over the full URL up to its partial end. */
  161.     for (Url = FullUrl; Url < Last; Url += 4)
  162.     {
  163.     Hash = (Hash << 4) ^ (Hash >> 28);
  164.     strncpy (Bytes, Url, 4);             /* Attention! There may be bytes remaining from the last action in the array */
  165.     Hash  ^= * (int *) Bytes;            /* But as their values are deterministic we can use them... */
  166.     debug (D_HASH, ("0x%08x - 0x%08x (%c%c%c%c)\n", * (int *) Bytes, Hash, Bytes[0], Bytes[1], Bytes[2], Bytes[3]));
  167.     }
  168. #if (MAX_FILE_LEN < 12)
  169. #   error Only filelenghts >= 12 is supported right now
  170. #endif
  171.     debug (D_HASH, ("-> 0x%08x\n", Hash));
  172.     sprintf (*DirPtr, "@!%08x", Hash);
  173.  
  174.     *UrlPtr  = Last + 1;
  175.     *DirPtr += 10;
  176.  
  177.     if (*Last == '/')
  178.     dreturn (D_CACHE, 4);
  179.     dreturn (D_CACHE, 2);
  180. }
  181.  
  182. /*)) */
  183. /*(( "HashRemaining ()" */
  184.  
  185. /* private: Hash remaining url name.
  186.  * returns: 0: not possible, 2: final filname part reached */
  187. /* Url and Dir pointers are updated accordingly (only on return value != 0). */
  188.  
  189. static int HashRemaining (const char **UrlPtr, char **DirPtr, const char *FullUrl)
  190. {
  191.     const char *Url, *Last;
  192.     int  Hash = 0;
  193.     char __aligned Bytes [4] = { 0, 0, 0, 0 };
  194. #if (sizeof(int) != 4)
  195. #   error Only 4 byte ints supported!
  196. #endif
  197.  
  198.     Last = *UrlPtr + strlen (*UrlPtr);
  199.  
  200.     /* Hash values are always computed over the full URL. */
  201.     for (Url = FullUrl; Url < Last; Url += 4)
  202.     {
  203.     Hash = (Hash << 4) ^ (Hash >> 28);
  204.     strncpy (Bytes, Url, 4);             /* Attention! There may be bytes remaining from the last action in the array */
  205.     Hash  ^= * (int *) Bytes;            /* But as their values are deterministic we can use them... */
  206.     debug (D_HASH, ("0x%08x - 0x%08x (%c%c%c%c)\n", * (int *) Bytes, Hash, Bytes[0], Bytes[1], Bytes[2], Bytes[3]));
  207.     }
  208. #if (MAX_FILE_LEN < 12)
  209. #   error Only filelenghts >= 12 is supported right now
  210. #endif
  211.     debug (D_HASH, ("=> 0x%08x\n", Hash));
  212.     sprintf (*DirPtr, "@!%08x", Hash);
  213.  
  214.     *UrlPtr  = Last + 1;
  215.     *DirPtr += 10;
  216.  
  217.     dreturn (D_CACHE, 2);
  218. }
  219.  
  220. /*)) */
  221. /*(( "ReadFile ()" */
  222.  
  223. /* Read file contents into static (!) buffer. Maximum length: MAX_URLBUFFER. String is always 0-terminated.
  224.  * Returns NULL on error. */
  225.  
  226. static char *ReadFile (const char *name)
  227. {
  228.     static char Buffer [MAX_URLBUFFER];
  229.     BPTR file;
  230.     long len;
  231.  
  232.     Buffer [0] = '\0';
  233.     if (! (file = Open ((char *) name, MODE_OLDFILE)))
  234.     return NULL;
  235.     if ( (len = Read (file, Buffer, MAX_URLBUFFER-1)) <= 0)
  236.     {
  237.     Close (file);
  238.     return NULL;
  239.     }
  240.     Close (file);
  241.     Buffer [len] = '\0';
  242.  
  243.     return Buffer;
  244. }
  245.  
  246. /*)) */
  247. /*(( "DeleteFiles ()" */
  248.  
  249. /* Delete all files inside a directory. */
  250. /* Returns 0 on *major* failure. */
  251.  
  252. int DeleteFiles (const char *Dir)
  253. {
  254.     BPTR lock, olddir;
  255.     struct ExAllData *E;
  256.     struct ExAllControl *ExAllCtrl;
  257.     char   Buffer [512];
  258.     int    More;
  259.  
  260.     debug (D_FILES, ("scanning '%s'\n", Dir));
  261.     if (! (lock = Lock ((char *) Dir, ACCESS_READ)))
  262.     dreturn (D_FILES, 0);
  263.  
  264.     if (! (ExAllCtrl = (struct ExAllControl *) AllocDosObject(DOS_EXALLCONTROL,NULL)))
  265.     {
  266.     PrintFault (IoErr (), "can't alloc dosobject");
  267.     UnLock (lock);
  268.     dreturn (D_FILES, 1);
  269.     }
  270.     olddir = CurrentDir (lock);
  271.  
  272.     do
  273.     {
  274.     More = ExAll (lock, (struct ExAllData *) Buffer, sizeof (Buffer), ED_NAME, ExAllCtrl);
  275.     if (! More && IoErr () != ERROR_NO_MORE_ENTRIES)
  276.     {
  277.         PrintFault (IoErr (), "ExAll() Error");
  278.         break;
  279.     }
  280.     if (! ExAllCtrl->eac_Entries)
  281.         continue;
  282.     E = (struct ExAllData *) Buffer;
  283.     while (E)
  284.     {
  285.         debug (D_FILES, ("deleting '%s'\n", E->ed_Name));
  286.         if (! DeleteFile (E->ed_Name))
  287.         LogErr (NULL, L_ERROR, NULL, -1, "on deleting temporary file '%s'", E->ed_Name);
  288.         else
  289.         LogErr (NULL, L_INFO, NULL, 0, "deleting old temporary file '%s'", E->ed_Name);
  290.         E = E->ed_Next;
  291.     }
  292.     } while (More);
  293.  
  294.     CurrentDir (olddir);
  295.     FreeDosObject (DOS_EXALLCONTROL, ExAllCtrl);
  296.     UnLock (lock);
  297.     dreturn (D_FILES, 1);
  298. }
  299. /*)) */
  300.  
  301. /*(( "CacheInit () / CacheExit ()" */
  302.  
  303. /* Initialize cache. Initialize invalid char table. Delete all files in @temp directory. */
  304.  
  305. int CacheInit (void)
  306. {
  307.     BPTR lock;
  308.     int    i, e;
  309.  
  310.     /* Test existance of CACHEDIRVALIDFILE and @trash directory */
  311.     lock = Lock (CACHEDIRVALIDFILE, ACCESS_READ);
  312.     if (! lock)
  313.     {
  314.     PrintFault (IoErr (), "can't lock '" CACHEDIRVALIDFILE "' - seems to be the wrong directory");
  315.     return 0;
  316.     }
  317.     UnLock (lock);
  318.     if (! (lock = Lock ("@trash", ACCESS_READ)))
  319.     {
  320.     PrintFault (IoErr (), "can't lock '@trash' dir");
  321.     return 0;
  322.     }
  323.     UnLock (lock);
  324.  
  325.     /* Initialize invalid char table. Only the first pass may needs adaption for more invalid chars. */
  326.     /* invalid chars: control characters, 127, 255, 32-34(' !"'), 160(' '), '@', '\0', ':', '/' */
  327.     /* Perhaps we should disallow the following to (according to the DOS manual - but nevertheless,
  328.      * they work well, so we don't do anything about them...
  329.      * *not* escaped: ';', '*', '?', '`', '#', '%' */
  330.  
  331.     /* first pass: mark invalid chars */
  332.     for (i = 0; i <= 34; i++)        /* control characters, 32-34, 0 */
  333.     InvalidChars [i] = 1;
  334.     for (i = 127; i <= 160; i++)     /* control characters 2., 127, 160 */
  335.     InvalidChars [i] = 1;
  336.     InvalidChars [255] = InvalidChars ['@'] = InvalidChars [':'] = InvalidChars ['/'] = 1;
  337.  
  338.     /* second pass: set marked chars (e is current escape character) */
  339.     for (e = 0; e < 256; e++)
  340.     if (! InvalidChars [e])
  341.         break;
  342.     for (i = 0; i < 256; i++)
  343.     if (InvalidChars [i])
  344.     {
  345.         assert (e < 256);
  346.         debug (D_HASH, ("Escaping char 0x%02x by '%c'\n", i, e));
  347.         EscapedChars [e] = i;
  348.         InvalidChars [i] = e;
  349.         do
  350.         e++;
  351.         while (e < 256 && (InvalidChars [e] || EscapedChars [tolower (e)] || EscapedChars [toupper (e)]));
  352.     }
  353.  
  354.     /* Delete all files in @temp and @trash */
  355.     if (! DeleteFiles ("@temp"))
  356.     {
  357.     PrintFault (IoErr (), "error while cleaning '@temp' dir");
  358.     return 0;
  359.     }
  360.     if (! DeleteFiles ("@trash"))
  361.     {
  362.     PrintFault (IoErr (), "error while cleaning '@trash' dir");
  363.     return 0;
  364.     }
  365.  
  366.     return 1;
  367. }
  368.  
  369.  
  370. void CacheExit (void)
  371. {
  372. }
  373.  
  374. /*)) */
  375. /*(( "CacheGet ()" */
  376.  
  377. /* the *major* one: check cache url, construct cache filename, check ambiguity */
  378.  
  379. int CacheGet (cachefile_t *Cache, const char *Url, request_t *Req)
  380. {
  381.     char *Dir;
  382.     const char *UrlDone, *UrlThis;
  383.     char       *DirDone, *DirThis;
  384.     char *Check;
  385.     BPTR lock;
  386.     BPTR file;
  387.     int  retval, PartNumber;
  388.     static int TempNumber = 0;
  389.     __aligned struct FileInfoBlock Fib;      /* too lazy to use AllocDosObject() and do cleanups... */
  390.  
  391.     assert (Cache);
  392.     assert (Url);
  393.     assert (Cache->File == NULL);
  394.     Cache->Req = Req;
  395.     Dir = Cache->FileName;
  396.  
  397.     UrlDone  = Url;
  398.     DirDone  = Dir;
  399.     *DirDone = '\0';
  400.     PartNumber = 0;
  401.     TempNumber++;
  402.     for (;;)
  403.     {
  404.     UrlThis = UrlDone;
  405.     DirThis = DirDone;
  406.     if (DirDone - Dir > MAX_PATH_LEN - MAX_FILE_LEN - 3 ||
  407.         PartNumber >= MAX_FILE_DEPTH-1)           /* Url is too long -> Hash remaining */
  408.     {
  409.         if (! (retval = HashRemaining (&UrlDone, &DirDone, Url)) )
  410.         dreturn (D_CACHE, -1);
  411.     }
  412.     else if (! (retval = EscapePartial (&UrlDone, &DirDone, PartNumber)) )   /* try escaping forbidden characters */
  413.     {
  414.         if (PartNumber < 1)                                                  /* on escaping 1st or 2nd part: no special parts anymore! */
  415.         PartNumber = 1;
  416.         if (! (retval = HashPartial (&UrlDone, &DirDone, Url, PartNumber)) ) /* failed -> hash partial */
  417.         if (! (retval = HashRemaining (&UrlDone, &DirDone, Url)) )       /* failed -> hash remaining */
  418.             dreturn (D_CACHE, -1);
  419.     }
  420.  
  421.  
  422.     switch (retval) {
  423.     case 1:                                                        /**** filename part (last part) was reached ****/
  424.         *DirDone++ = '@';
  425.         *DirDone   = '\0';
  426.         if (! (lock = Lock (Dir, ACCESS_READ)) )
  427.         dreturn (D_CACHE, 0);                                  /* no file so far */
  428.         if (! Examine (lock, &Fib))
  429.         {
  430.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on examing '%s'", Dir);
  431.         UnLock (lock);
  432.         dreturn (D_CACHE, -1);
  433.         }
  434.         UnLock (lock);
  435.         if (strcmp (DirThis, Fib.fib_FileName) == 0)               /* ambiguity? (it is sufficient here to check the filename only) */
  436.         {
  437. #if (MAX_TEMP_LEN < 17)
  438. #   error Only temp path lengths >= 17 supported
  439. #endif
  440.         sprintf (Cache->TempName, "@temp/rq%08x", TempNumber++); /* new file */
  441.         dreturn (D_CACHE, 1);
  442.         }
  443.         UrlDone = UrlThis;
  444.         DirDone = DirThis;
  445.         if (! (retval = HashRemaining (&UrlDone, &DirDone, Url)) ) /* failed -> hash remaining */
  446.         dreturn (D_CACHE, -1);
  447.         /* no break! */                                        /* new name -> have to make another ambiguity check! */
  448.  
  449.  
  450.     case 2:                                                        /**** filename part (last part) as hash ****/
  451.         *DirDone++ = '@';
  452.         *DirDone   = '\0';
  453.  
  454.         DirThis [1] = '@';                                         /* check URL file */
  455.         if ( (Check = ReadFile (Dir)) )
  456.         {
  457.         DirThis [1] = '!';
  458.         if (strcmp (Check, Url) == 0)
  459.         {
  460.             if (! (lock = Lock (Dir, ACCESS_READ)) )
  461.             dreturn (D_CACHE, 0);                          /* no file so far ?!? */
  462.             UnLock (lock);
  463.             sprintf (Cache->TempName, "@temp/@!%08x", TempNumber++); /* new file */
  464.             dreturn (D_CACHE, 1);
  465.         }
  466.         else
  467.             dreturn (D_CACHE, -1);
  468.         }
  469.         DirThis [1] = '!';
  470.  
  471.         dreturn (D_CACHE, 0);
  472.  
  473.  
  474.     case 3:                                                        /**** partial directory name ****/
  475.         if (! (lock = Lock (Dir, ACCESS_READ)) )                   /* check availability / ambiguity */
  476.         {                                                          /* not yet there -> create (no ambiguity check needed) */
  477.         if (! (lock = CreateDir (Dir)) )
  478.         {
  479.             LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating dir '%s'", Dir);
  480.             dreturn (D_CACHE, -1);
  481.         }
  482.         UnLock (lock);
  483.  
  484.         strcpy (DirDone, "/@dirurl");                           /* create directory url filename */
  485.         if (! (file = Open (Dir, MODE_NEWFILE)))
  486.         {
  487.             LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating dirfile '%s'", Dir);
  488.             *DirDone = '\0';
  489.             DeleteFile (Dir);
  490.             dreturn (D_CACHE, -1);
  491.         }
  492.         Write (file, (char *) Url, UrlDone - Url);
  493.         if (! Close (file))
  494.             LogErr (Cache->Req, L_ERROR, NULL, -1, "writing to dirfile in '%s' - may be inconsistent", Dir);
  495.         *++DirDone   = '\0';                                   /* '/' was already added by strcpy... ;) */
  496.         break;
  497.         }
  498.         UnLock (lock);
  499.         strcpy (DirDone, "/@dirurl");                              /* create directory url filename */
  500.         if (! (Check = ReadFile (Dir)))
  501.         {
  502.         LogErr (NULL, L_ERROR, NULL, -1, "on reading file '%s'", Dir);
  503.         dreturn (D_CACHE, -1);
  504.         }
  505.         *++DirDone   = '\0';                                       /* '/' was already added by strcpy... ;) */
  506.         if (strncmp (Url, Check, strlen (Check)) == 0)             /* ambiguity? */
  507.         break;
  508.         UrlDone = UrlThis;
  509.         DirDone = DirThis;
  510.         if (! (retval = HashPartial (&UrlDone, &DirDone, Url, PartNumber)) ) /* failed -> hash dir name */
  511.         dreturn (D_CACHE, -1);
  512.         /* no break! */                                        /* new name -> have to make another ambiguity check! */
  513.  
  514.  
  515.     case 4:                                                        /**** partial directory name ****/
  516.         DirThis [1] = '@';                                         /* check URL file */
  517.         if ( (Check = ReadFile (Dir)) )
  518.         {
  519.         DirThis [1] = '!';                                     /* Url file there -> check ambiguity */
  520.  
  521.         if (strncmp (Check, Url, strlen (Check)) == 0)
  522.         {
  523.             if (! (lock = Lock (Dir, ACCESS_READ)) )           /* should be already there, but check it anyway */
  524.             {
  525.             LogErr (Cache->Req, L_INFO, NULL, -1, "dir '%s' was lost", Dir);
  526.             DirThis [1] = '@';
  527.             DeleteFile (Dir);                              /* Try to clean up */
  528.             dreturn (D_CACHE, -1);
  529.             }
  530.             UnLock (lock);
  531.             *DirDone++ = '/';
  532.             *DirDone   = '\0';
  533.             break;
  534.         }
  535.         else
  536.             dreturn (D_CACHE, -1);
  537.         }
  538.  
  539.         if (! (file = Open (Dir, MODE_NEWFILE)) )                  /* no URL file -> create new one */
  540.         {
  541.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating hash urlfile '%s'", Dir);
  542.         dreturn (D_CACHE, -1);
  543.         }
  544.         Write (file, (char *) Url, UrlDone - Url);
  545.         if (! Close (file))
  546.         {
  547.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating hash urlfile '%s'", Dir);
  548.         dreturn (D_CACHE, -1);
  549.         }
  550.         DirThis [1] = '!';
  551.  
  552.         if (! (lock = CreateDir (Dir)) )
  553.         {
  554.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating dir '%s'", Dir);
  555.         DirThis [1] = '@';
  556.         DeleteFile (Dir);                                      /* try to clean up */
  557.         dreturn (D_CACHE, -1);
  558.         }
  559.         UnLock (lock);
  560.  
  561.         strcpy (DirDone, "/@dirurl");                              /* create directory url filename */
  562.         if (! (file = Open (Dir, MODE_NEWFILE)))
  563.         {
  564.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating dirfile '%s'", Dir);
  565.         dreturn (D_CACHE, -1);
  566.         }
  567.         Write (file, (char *) Url, UrlDone - Url);
  568.         if (! Close (file))
  569.         LogErr (Cache->Req, L_ERROR, NULL, -1, "writing to dirfile in '%s' - may be inconsistent", Dir);
  570.  
  571.         *++DirDone   = '\0';                                       /* '/' was already added by strcpy... ;) */
  572.         break;
  573.  
  574.  
  575.     default:
  576.         assert (0);
  577.         break;
  578.     }
  579.  
  580.     PartNumber++;
  581.     }
  582.     /* NOTREACHED */
  583. }
  584.  
  585. /*)) */
  586. /*(( "CacheOpenOld ()" */
  587.  
  588. /* Open existing cache file. */
  589.  
  590. int CacheOpenOld (cachefile_t *Cache)
  591. {
  592.     assert (Cache->File == NULL);
  593.     Cache->TempName [0] = '\0';
  594.     debug (D_FILES, ("open old cache '%s'\n", Cache->File));
  595.     if (! (Cache->File = Open (Cache->FileName, MODE_OLDFILE)))
  596.     {
  597.     LogErr (Cache->Req, L_ERROR, NULL, -1, "on opening old cache file '%s'", Cache->FileName);
  598.     return -1;
  599.     }
  600.     return 0;
  601. }
  602.  
  603. /*)) */
  604. /*(( "CacheOpenNew ()" */
  605.  
  606. /* Open new cache file. */
  607.  
  608. int CacheOpenNew (cachefile_t *Cache, const char *Url)
  609. {
  610.     char *name, *Part;
  611.     BPTR file;
  612.  
  613.     assert (Cache->File == NULL);
  614.     name  = Cache->TempName [0] ? Cache->TempName : Cache->FileName;
  615.     debug (D_FILES, ("open new cache '%s', final name '%s'\n", name, Cache->FileName));
  616.  
  617.     if (! (Cache->File = Open (name, MODE_NEWFILE)))
  618.     {
  619.     LogErr (Cache->Req, L_ERROR, NULL, -1, "on opening new cache file '%s'", name);
  620.     return -1;
  621.     }
  622.     if (! ChangeMode (CHANGE_FH, Cache->File, MODE_READWRITE))
  623.     LogErr (Cache->Req, L_WARN, NULL, -1, "ChangeMode() failed for file '%s'", name);
  624.  
  625.     /* check for hashed file */
  626.     if ( (Part = strrchr (name, '/')) )
  627.     Part++;
  628.     else
  629.     Part = name;
  630.     if (Part [0] == '@' && Part [1] == '!')
  631.     {
  632.     assert (Part [2] != '\0');                         /* should *not* be a (empty name) directory... */
  633.  
  634.     Part [1] = '@';                                    /* Create (perhaps temporary) Url file */
  635.     if (! (file = Open (name, MODE_NEWFILE)) )
  636.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating temporary hash urlfile '%s'", name);
  637.     else
  638.     {
  639.         Write (file, (char *) Url, (long) strlen (Url));
  640.         if (! Close (file))
  641.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on creating temporary hash urlfile '%s'", name);
  642.     }
  643.     Part [1] = '!';
  644.     }
  645.     return 0;
  646. }
  647.  
  648. /*)) */
  649. /*(( "CacheClose ()" */
  650.  
  651. /* Close cache file, move files, tidy up. */
  652.  
  653. void CacheClose (cachefile_t *Cache, int Ok)
  654. {
  655.     static short TrashNumber = 0;          /* needed for trash files (accessed files to be removed)... */
  656.  
  657.     debug (D_FILES, ("closing cache file '%s', temp name '%s', ok:%s\n", Cache->FileName, Cache->TempName, Ok ? "yes" : "no"));
  658.     if (Cache->File)
  659.     {
  660.     BPTR lock;
  661.     char *Part, *PTmp;
  662.     char *name = Cache->TempName [0] ? Cache->TempName : Cache->FileName;
  663.     char TrashName [16];
  664.  
  665.     if ( (PTmp = strrchr (name, '/')) )
  666.         PTmp++;
  667.     else
  668.         PTmp = name;
  669.  
  670.     if (Seek (Cache->File, 0, OFFSET_CURRENT) == 0)                    /* Delete empty files */
  671.     {
  672.         if (Ok)
  673.         LogErr (Cache->Req, L_INFO, NULL, 0, "removing empty cache file");
  674.         Ok = 0;
  675.     }
  676.  
  677.     if (! Close (Cache->File))
  678.         LogErr (Cache->Req, L_ERROR, NULL, -1, "on closing cache file '%s'", name);
  679.     else
  680.     {
  681.         if (Ok && Cache->TempName [0])
  682.         {
  683.         if ( (Part = strrchr (Cache->FileName, '/')) )             /* PTmp is used on TempName */
  684.             Part++;
  685.         else
  686.             Part = Cache->FileName;
  687.  
  688.         if (Part [0] == '@' && Part [1] == '!')                    /* hashed file (the *real* name is tested!) */
  689.         {
  690.             debug (D_FILES, ("creating hash url file\n"));
  691.             assert (Part [2] != '\0');
  692.             Part [1] = PTmp [1] = '@';                             /* check URL file */
  693.             if ( (lock = Lock (Cache->FileName, ACCESS_READ)) )
  694.             {
  695.             UnLock (lock);
  696.             if (! DeleteFile (Cache->FileName))                         /* remove old url file (may be ambigous) */
  697.             {
  698.                 sprintf (TrashName, "@trash/o%04x", TrashNumber++);     /* the file is open from another connection */
  699.                 if (! Rename (Cache->FileName, TrashName))              /* so we move it to the trash directory (httpdelete will kill it...) */
  700.                 LogErr (Cache->Req, L_ERROR, NULL, -1, "on trashing hash url file '%s'", Cache->FileName);
  701.             }
  702.             }
  703.             if (! Rename (Cache->TempName, Cache->FileName))
  704.             {
  705.             LogErr (Cache->Req, L_ERROR, NULL, -1, "on renaming hash url file '%s'", name);
  706.             Ok = 0;
  707.             }
  708.             Part [1] = PTmp [1] = '!';
  709.         }
  710.  
  711.         if (Ok)
  712.         {
  713.             if ( (lock = Lock (Cache->FileName, ACCESS_READ)) )
  714.             {
  715.             UnLock (lock);
  716.             if (! DeleteFile (Cache->FileName))                         /* remove old cache file */
  717.             {
  718.                 sprintf (TrashName, "@trash/o%04x", TrashNumber++);     /* the file is open from another connection */
  719.                 if (! Rename (Cache->FileName, TrashName))              /* so we move it to the trash directory (httpdelete will kill it...) */
  720.                 LogErr (Cache->Req, L_ERROR, NULL, -1, "on trashing cache file '%s'", Cache->FileName);
  721.             }
  722.             }
  723.             if (! Rename (name, Cache->FileName))
  724.             LogErr (Cache->Req, L_ERROR, NULL, -1, "on renaming cache file '%s'", name);
  725.             /* name = Cache->FileName; */    /* not needed... */
  726.         }
  727.         }
  728.     }
  729.     if (! Ok)
  730.     {
  731.         debug (D_FILES, ("deleting cache file '%s'\n", name));
  732.         if (! DeleteFile (name))                                    /* remove old cache file */
  733.         {
  734.         sprintf (TrashName, "@trash/o%04x", TrashNumber++);     /* the file is open from another connection */
  735.         if (! Rename (name, TrashName))                         /* so we move it to the trash directory (httpdelete will kill it...) */
  736.             LogErr (Cache->Req, L_ERROR, NULL, -1, "on trashing cache file '%s'", name);
  737.         }
  738.         if (PTmp [0] == '@' && PTmp [1] == '!')
  739.         {
  740.         debug (D_FILES, ("deleting cache url file\n"));
  741.         assert (PTmp [2] != '\0');
  742.         PTmp [1] = '@';
  743.         if (! DeleteFile (name))                                /* remove old hash urlfile */
  744.         {
  745.             sprintf (TrashName, "@trash/o%04x", TrashNumber++); /* the file is open from another connection */
  746.             Rename (name, TrashName);
  747.         }
  748.             /* PTmp [1] = '!'; */     /* not needed... */
  749.         }
  750.     }
  751.     Cache->File = NULL;
  752.     }
  753.     Cache->TempName [0] = '\0';
  754.     Cache->Req          = NULL;          /* you never know... */
  755. }
  756.  
  757. /*)) */
  758. /*(( "CacheRead () / CacheWrite ()" */
  759.  
  760. /* read something from cache file */
  761.  
  762. int CacheRead (cachefile_t *Cache, void *Buf, int Len)
  763. {
  764.     if (Cache->File != NULL)
  765.     return (Read (Cache->File, Buf, (long) Len));
  766.     return 0;
  767. }
  768.  
  769.  
  770. /* write something to cache file */
  771.  
  772. int CacheWrite (cachefile_t *Cache, const void *Buf, int Len)
  773. {
  774.     if (Cache->File != NULL)
  775.     return (Write (Cache->File, (void *) Buf, (long) Len));
  776.     return Len;
  777. }
  778.  
  779. /*)) */
  780.  
  781. /*(( "CacheResolve ()" */
  782.  
  783. /* get url name from cache file */
  784.  
  785. int CacheResolve (const char *File, char *Url)
  786. {
  787.     char Dir [MAX_PATH_LEN];
  788.     const char *c;
  789.     char *cp;
  790.  
  791.     strcpy (Dir, File);
  792.     if ( (cp = strrchr (Dir, '/')) )
  793.     cp++;
  794.     else
  795.     cp = Dir;
  796.     strcpy (cp, "@dirurl");
  797.     if (! (c = ReadFile (Dir)) && cp != Dir)        /* ReadFile may return NULL on empty root @dirurl */
  798.     dreturn (D_CACHE, 0);
  799.     *cp = '\0';
  800.     strcpy (Url, c);
  801.     cp = Url + strlen (Url);
  802.     if ( (c = strrchr (File, '/')))
  803.     c++;
  804.     else
  805.     c = File;
  806.     switch (*c) {
  807.     case 0:
  808.     dreturn (D_CACHE, 0);             /* 'file' was a directory */
  809.  
  810.     case '@':
  811.     switch (*++c) {
  812.     case '!':
  813.         if (c [1] == '\0')            /* empty directory file */
  814.         dreturn (D_CACHE, 0);     /* should only occure as directory */
  815.         strcpy (Dir, File);
  816.         Dir [c - File] = '@';
  817.         if (! (c = ReadFile (Dir)) )
  818.         dreturn (D_CACHE, 0);
  819.         strcpy (Url, c);
  820.         dreturn (D_CACHE, 1);
  821.  
  822.     case '@':                         /* this is a hash url file */
  823.         dreturn (D_CACHE, 0);
  824.  
  825.     case 0:
  826.         *cp = '\0';                   /* empty filename: Urls http://abc.cde/xxxx/yyyy/ <- */
  827.         dreturn (D_CACHE, 1);
  828.  
  829.     default:
  830.         dreturn (D_CACHE, 0);         /* special files */
  831.     }
  832.  
  833.     default:
  834.     for (;;)
  835.         switch ( (*cp++ = *c++) ) {
  836.         case '@':
  837.         if (*c != '\0')
  838.             dreturn (D_CACHE, 0);
  839.         cp [-1] = '\0';
  840.         dreturn (D_CACHE, 1);
  841.  
  842.         case '!':
  843.         cp [-1] = EscapedChars [c[-1]];
  844.         assert (InvalidChars [EscapedChars [c[-1]]]);   /* only invalid chars *have* to be escaped! */
  845.         break;
  846.  
  847.         case 0:
  848.         dreturn (D_CACHE, 0);                           /* file was a directory */
  849.  
  850.         /* default: continue */
  851.         }
  852.     }
  853. }
  854.  
  855. /*)) */
  856. /*(( "CacheRemove ()" */
  857.  
  858. /* Delete cache file */
  859.  
  860. int CacheRemove (const char *File, int DoDelete)
  861. {
  862.     char Related [MAX_PATH_LEN];
  863.     const char *Part;
  864.     BPTR lock;
  865.  
  866.     if ( (Part = strrchr (File, '/')) )      /* begin of filename part */
  867.     Part++;
  868.     else
  869.     Part = File;
  870.  
  871.     if (strncmp (File, "@trash/", 7) == 0)   /* files in the trash directory are always allowed to be deleted */
  872.     {
  873.     if (DoDelete)
  874.         if (! DeleteFile ((char *) File))
  875.         {
  876.         if (IoErr () != 205)
  877.             return 0;
  878.         return 4;                    /* object not found */
  879.         }
  880.     return 1;
  881.     }
  882.  
  883.     if (Part [0] == '@')
  884.     {
  885.     if (Part [1] != '!')
  886.     {
  887.         if (strcmp (Part, "@dirurl") == 0)   /* directory special file */
  888.         return 2;
  889.         if (Part [1] == '@')                 /* url related file */
  890.         {
  891.         strcpy (Related, File);
  892.         Related [Part - File + 1] = '!'; /* related hash file */
  893.         if ( (lock = Lock (Related, ACCESS_READ)))
  894.         {
  895.             UnLock (lock);
  896.             return 5;
  897.         }
  898.         if (! DoDelete)                  /* hashed file is lost... */
  899.             return 7;
  900.         }
  901.         else if (Part [1] != '\0')           /* no empty name file */
  902.         return 6;
  903.     }
  904.     else
  905.     {
  906.         strcpy (Related, File);
  907.         Related [Part - File + 1] = '@';     /* related url file */
  908.         if (! (lock = Lock (Related, ACCESS_READ)))
  909.         if (! DoDelete)
  910.             return 7;                    /* url file is lost... */
  911.         UnLock (lock);
  912.     }
  913.     }
  914.     if (! DoDelete)
  915.     return 1;
  916.  
  917.     /* TODO: when a file cannot be deleted it should be renamed into a trash directory */
  918.  
  919.     if (Part [0] == '@' && Part [1] == '!')  /* hashed file */
  920.     {
  921.     strcpy (Related, File);
  922.     Related [Part - File + 1] = '@';     /* url name file */
  923.     if (! DeleteFile (Related))
  924.     {
  925.         if (IoErr () != 205)             /* don't worry when object not found */
  926.         return 0;
  927.     }
  928.     }
  929.     if (! DeleteFile ((char *) File))
  930.     {
  931.     if (IoErr () != 205)
  932.         return 0;
  933.     return 4;                            /* object not found */
  934.     }
  935.     return 1;
  936. }
  937.  
  938. /*)) */
  939. /*(( "CacheRemoveDir ()" */
  940.  
  941. /* Delete cache dir */
  942.  
  943. int CacheRemoveDir (const char *Dir, int DoDelete)
  944. {
  945.     char File [MAX_PATH_LEN];
  946.     char *Part;
  947.     const char *Check;
  948.  
  949.     /* check whether it was a hashed directory */
  950.     if ( (Check = strrchr (Dir, '/')) )      /* begin of filename part */
  951.     Check++;
  952.     else
  953.     Check = Dir;
  954.  
  955.     if (Check [0] == '@' && Check [1] != '!')
  956.     {
  957.     if (strncmp (Dir, "@trash/", 7) == 0)
  958.     {
  959.         if (! DoDelete)             /* scan trash dir always */
  960.         return 1;
  961.         return 2;                   /* but do never ever delete it */
  962.     }
  963.     return 2;
  964.     }
  965.     if (! DoDelete)
  966.     return 1;
  967.  
  968.     strcpy (File, Dir);
  969.     Part = File + strlen (File);
  970.     strcpy (Part, "/@dirurl");
  971.  
  972.     if (! DeleteFile (File))
  973.     {
  974.     if (IoErr () != 205)      /* if object not found, don't worry */
  975.         return 0;
  976.     }
  977.     *Part = '\0';
  978.     if (! DeleteFile ((char *) Dir))
  979.     return 0;
  980.  
  981.  
  982.     if (Check [0] == '@' && Check [1] == '!')
  983.     {
  984.     File [Check - Dir + 1] = '@';       /* File contains a copy of Dir */
  985.     if (! DeleteFile ((char *) File))   /* delete related file */
  986.         return 0;
  987.     }
  988.  
  989.     return 1;
  990. }
  991.  
  992. /*)) */
  993.  
  994.